package com.monkey.luffer.springboot.demo.basis.kcc.test.lianji.majiang;

import lombok.Data;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author jiahong.liu
 */
@Data
public class Scheduler {
    List<Player> players = new ArrayList<>();
    private int zhuangJia = 3;
    private int handOutIndex = 3;
    private boolean hu = false;
    private boolean isPeng = false;
    private Majiang.Card curCard = null;
    private boolean isBegin;
    Majiang majiang = new Majiang();
    List<Socket> sockets = new ArrayList<>();
    int robotNum = 0;

    public Scheduler(int robotNum) {
        this.robotNum = robotNum;
    }

    public void addPlayer(Socket socket) throws InterruptedException, IOException {
        if (robotNum == 4) {
            this.sockets.add(socket);
            String name = "Visitor" + players.size();
            sendMessage(socket, ("您的名字为：" + name + "\n").getBytes(StandardCharsets.UTF_16));
            for (int i = 0; i < 4; i++) {
                isBegin = true;
                players.add(new Player("player" + i, Player.Seat.getSortByNum(i)));
            }
            sendMessage("游戏开始\n".getBytes(StandardCharsets.UTF_16));
            begin();
        } else {
            this.sockets.add(socket);
            String name = "Player" + players.size();
            Player player = new Player(name, socket, true, Player.Seat.getSortByNum(players.size()));
            players.add(player);
            sendMessage(socket, ("您的名字为：" + name + "\n").getBytes(StandardCharsets.UTF_16));
            if (players.size() == 4 - robotNum) {
                isBegin = true;
                sendMessage("游戏开始\n".getBytes(StandardCharsets.UTF_16));
                for (int i = 4 - robotNum; i < 4; i++) {
                    players.add(new Player("player" + i, Player.Seat.getSortByNum(i)));
                }
                sendMessage("A请输入开始进行对局\n".getBytes(StandardCharsets.UTF_16));
                restart();
//                begin();
            }
        }
    }

    public void clear() {
        for (Player player: players) {
            player.getPlayerHand().clearAll();
        }

        hu = false;
        isPeng = false;
        curCard = null;
    }


    public void printScore() {
        for (Player player: players) {
            sendMessage((player.getName() + " 分数为："+ player.getScore() + "\n").getBytes(StandardCharsets.UTF_16));
        }
    }

    public void begin() throws InterruptedException, IOException {
        clear();
        printScore();
        majiang = new Majiang();
        sendMessage("座位顺序为：\n".getBytes(StandardCharsets.UTF_16));
        sendMessage(("庄家为：" + players.get(zhuangJia).name + "\n").getBytes(StandardCharsets.UTF_16));
        sendMessage("现在开始发牌\n".getBytes(StandardCharsets.UTF_16));
        handOut();

        sendMessage("庄家先出牌：\n".getBytes(StandardCharsets.UTF_16));
        Player.PlayerStatus status = players.get(handOutIndex).checkIfGetCard(majiang.dealCard(), true, handOutIndex);
        handleStatus(status, players.get(handOutIndex), handOutIndex);
        if (hu) {
            sendMessage("A庄家胡牌，对局结束，请输入开始重新对局\n".getBytes(StandardCharsets.UTF_16));
            players.get(handOutIndex).setScore(players.get(handOutIndex).getScore() + 1);
            restart();
            return;
        }

        while (true) {
            if(majiang.isEnd()) {
                sendMessage("A牌已经发完，对局结束，请输入开始重新对局\n".getBytes(StandardCharsets.UTF_16));
                restart();
                return;
            }

            int hasPeng = -1;
            int hasEat = -1;
            List<Integer> hasHu = new ArrayList<>();
            List<Player.PlayerStatus> huStatus = new ArrayList<>();
            Player.PlayerStatus pengStatus = null;
            Player.PlayerStatus eatStatus = null;
            for(int i = 0; i < players.size(); i++) {
                if (i != handOutIndex) {
                    Player.PlayerStatus status1 = players.get(i).checkIfGetCard(curCard, false,  handOutIndex);
                   if (status1.isCanGang() || status1.isCanPeng()
                           || status1.getStatus() == Player.Status.PENG
                           || status1.getStatus() == Player.Status.GANG
                           || status1.getStatus() == Player.Status.AN) {
                       pengStatus = status1;
                       hasPeng = i;
                   } else if (status1.isCanEat() || status1.getStatus() == Player.Status.EAT) {
                       eatStatus = status1;
                       hasEat = i;
                   } else if (status1.getStatus() == Player.Status.HU) {
                       hasHu.add(i);
                       huStatus.add(status1);
                   }
                }
            }

            if (hasPeng > -1) {
                if (players.get(hasPeng).isManual()) {
                    userInput(players.get(hasPeng), pengStatus);
                }
                handleStatus(pengStatus, players.get(hasPeng), hasPeng);
            } else if (hasEat > -1) {
                if (players.get(hasEat).isManual()) {
                    userInput(players.get(hasEat), eatStatus);
                }
                handleStatus(eatStatus, players.get(hasEat), hasEat);
            } else if (hasHu.size() > 0) {
                for(int i = 0; i < hasHu.size(); i++) {
                    handleStatus(huStatus.get(i), players.get(hasHu.get(i)), hasHu.get(i));
                }
            }


            if (hu) {
                sendMessage("A对局结束，请输入开始重新对局\n".getBytes(StandardCharsets.UTF_16));
                players.get(handOutIndex).setScore(players.get(handOutIndex).getScore() + 1);
                restart();
                return;
            }

            if (!isPeng) {
                // 没有peng的则下个人摸牌。
                Player.Seat seat = Player.Seat.getNext(handOutIndex);
                handOutIndex = seat.getSort();
                handleStatus(players.get(handOutIndex).checkIfGetCard(majiang.dealCard(), true, handOutIndex),
                        players.get(handOutIndex), handOutIndex);
            } else {
                // 碰了就已经出牌了
                isPeng = false;
            }
        }
    }

    public void restart() throws IOException, InterruptedException {
        int begin = 0;
        for (Socket socket: sockets) {
            BufferedReader ois = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String line;
            while (( line = ois.readLine()) != null) {
                if ("开始".equals(line)) {
                    begin++;
                    break;
                }
            }
        }

        if (begin == 4 - robotNum) {
            begin();
        }

    }

    public void sendMessage(byte[] by) {
        // 将服务器接收到的消息发送给除了发送方以外的其他客户端
        int i = 0;
        for (Socket socket : sockets) {
            BufferedOutputStream ps;
            try {
                ps = new BufferedOutputStream(socket.getOutputStream());
                // 写入输出流，将内容发送给客户端的输入流
                ps.write(by);
                ps.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public void sendMessage(byte[] by, Socket sk) {
        // 将服务器接收到的消息发送给除了发送方以外的其他客户端
        for (Socket socket : sockets) {
            if (socket == sk) {
                continue;
            }
            BufferedOutputStream ps = null;
            try {
                ps = new BufferedOutputStream(socket.getOutputStream());
                ps.write(by);
                ps.flush();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public void sendMessage(Socket socket, byte[] by) {
        // 将服务器接收到的消息发送给除了发送方以外的其他客户端
        BufferedOutputStream ps = null;
        try {
            ps = new BufferedOutputStream(socket.getOutputStream());
            ps.write(by);   // 写入输出流，将内容发送给客户端的输入流
            ps.flush();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }


    public void handleStatus(Player.PlayerStatus status, Player player, int index) throws InterruptedException {
        if (Player.Status.HU == status.getStatus()) {
            boolean ziMo = false;
            if (index  == handOutIndex) {
                ziMo = true;
            }
            hu = true;
            handOutIndex = index;
            if (ziMo) {
                sendMessage((""  + players.get(handOutIndex).getName() + " 自摸"+ status.getGetCard().printCard() + "!游戏结束\n").getBytes(StandardCharsets.UTF_16));
            } else {
                sendMessage((""  + players.get(handOutIndex).getName() + " 胡牌!游戏结束\n").getBytes(StandardCharsets.UTF_16));
            }
            sendMessage(("手牌为：" + players.get(handOutIndex).getPlayerHand().printCard() + "\n").getBytes(StandardCharsets.UTF_16));
            zhuangJia = players.indexOf(player);
            return;
        }

        if (Player.Status.AN == status.getStatus()) {
            sendMessage((player.getName() + ": 暗杠！\n").getBytes(StandardCharsets.UTF_16));
            handOutIndex = index;
            Player.PlayerStatus newStatus = player.checkIfGetCard(majiang.dealCard(), true, handOutIndex);
            handleStatus(newStatus, player, index);
            isPeng = true;
        }


        if (Player.Status.GANG == status.getStatus()) {
            String printCard = status.getGetCard() == null ? curCard.printCard(4) : status.getGetCard().printCard(4);
            sendMessage((player.getName() + ": 杠！" + printCard + "\n").getBytes(StandardCharsets.UTF_16));
            handOutIndex = index;
            Player.PlayerStatus newStatus = player.checkIfGetCard(majiang.dealCard(), true, handOutIndex);
            handleStatus(newStatus, player, index);
            isPeng = true;
        }

        if (Player.Status.PENG == status.getStatus()) {
            sendMessage((player.getName() + ": 碰！" + curCard.printCard(3) + "\n").getBytes(StandardCharsets.UTF_16));
            if (player.isManual) {
                curCard = putOutCard(player);
                handOutIndex = index;
                isPeng = true;
            } else {
                curCard = player.putOutCard(new ArrayList<>(player.getPlayerHand().getAll()));
                handOutIndex = index;
                isPeng = true;
                Thread.sleep(1000);
            }
            sendMessage((""+ player.getName() + "出牌：" + curCard.printCard() + "\n").getBytes(StandardCharsets.UTF_16));
        }

        if (Player.Status.EAT == status.getStatus()) {
            sendMessage((player.getName() + ": 吃！" + curCard.printCard() + " " + status.getEatCard().printCard() + "\n").getBytes(StandardCharsets.UTF_16));
            // 删除吃的牌
            player.playerHand.removeCard(status.getEatCard().card1);
            player.playerHand.removeCard(status.getEatCard().card2);
            if (player.isManual) {
                curCard = putOutCard(player);
                handOutIndex = index;
            } else {
                curCard = player.putOutCard(new ArrayList<>(player.getPlayerHand().getAll()));
                handOutIndex = index;
                Thread.sleep(1000);
            }
            sendMessage((""+ player.getName() + "出牌：" + curCard.printCard() + "\n").getBytes(StandardCharsets.UTF_16));
        }

        if (Player.Status.PASS == status.getStatus()) {
            if (handOutIndex == index) {
                if (player.isManual) {
                    curCard = putOutCard(player);
                } else {
                    curCard = player.putOutCard(new ArrayList<>(player.getPlayerHand().getAll()));
                    Thread.sleep(1000);
                }
                sendMessage((""+ player.getName() + "出牌：" + curCard.printCard() + "\n").getBytes(StandardCharsets.UTF_16));
            }
        }
    }


    private Majiang.Card putOutCard(Player player) {
        sendMessage(player.socket,("您的手牌为: " + player.getPlayerHand().printCard() + "\n").getBytes(StandardCharsets.UTF_16));
        sendMessage(player.socket, ("A您可以打出一张牌, 输入牌的名字即可\n").getBytes(StandardCharsets.UTF_16));
        Majiang.Card card = null;

        try {
            BufferedReader ois = new BufferedReader(new InputStreamReader(player.socket.getInputStream()));
            while (true) {
                String line;
                while (( line = ois.readLine()) != null) {
                    card = Majiang.Card.getCard(line.trim());
                    if (card == null) {
                        throw new Exception("");
                    }
                    if (!player.hasCard(card)) {
                        sendMessage(player.socket, ("输入的牌型不存在\n").getBytes(StandardCharsets.UTF_16));
                        return putOutCard(player);
                    }

                    player.putOutCard(card);
                    return card;
                }
            }
        } catch (Exception e) {
            System.out.println("输入异常");
            return putOutCard(player);
        }
    }

    private void handOut() {
        majiang.shuffle();
        for (int i = 0; i < 4; i++) {
            if (i != 3) {
                for (Player player : players) {
                    player.getPlayerHand().putInCard(majiang.dealCard());
                    player.getPlayerHand().putInCard(majiang.dealCard());
                    player.getPlayerHand().putInCard(majiang.dealCard());
                    player.getPlayerHand().putInCard(majiang.dealCard());
                }
            } else {
                for (Player player : players) {
                    player.getPlayerHand().putInCard(majiang.dealCard());
                }
            }
        }

        for (Player player : players) {
            if (player.isManual) {
                sendMessage(player.socket, ("您的手牌为: " + player.getPlayerHand().
                        printCard() + "\n").getBytes(StandardCharsets.UTF_16));
            }
        }
    }


    private void userInput (Player player, Player.PlayerStatus playerStatus) {
        PlayerHand playerHand = player.getPlayerHand();
        boolean canGang = playerStatus.isCanGang();
        boolean canPeng = playerStatus.isCanPeng();
        boolean canEat = playerStatus.isCanEat();
        Majiang.Card card = playerStatus.getCurCard();
        List<Player.TwoCard> eatArr = playerStatus.getCanEatCard();
        if(canGang && canEat) {
            sendMessage(player.getSocket(), ("您的手牌为：" + playerHand.printCard() + "\n").getBytes(StandardCharsets.UTF_16));
            sendMessage(player.getSocket(), ("A您可以杠或碰"  + card.printCard() + "输入 G-杠，Y-碰，E-吃，P-pass\n").getBytes(StandardCharsets.UTF_16));
            String line = waitMessage(player.getSocket());
            // 删除所有杠掉的牌
            if ("G".equalsIgnoreCase(line)) {
                playerHand.getAll().removeIf(integer -> integer.equals(card.getNumber() + card.getSuit().getSalt()));
                playerHand.setCardNum(playerHand.cardNum - 3);
                playerStatus.setStatus(Player.Status.GANG);
            } else if ("Y".equalsIgnoreCase(line)){
                playerHand.getAll().removeIf(integer -> integer.equals(card.getNumber() + card.getSuit().getSalt()));
                playerHand.getAll().add(card.getNumber() + card.getSuit().getSalt());
                playerHand.getPeng().add(card.getNumber() + card.getSuit().getSalt());
                Collections.sort(playerHand.getAll());
                playerHand.setCardNum(playerHand.cardNum - 2);
                playerStatus.setStatus(Player.Status.PENG);
            } else if("E".equalsIgnoreCase(line)) {
                eat(eatArr, playerStatus, player.getSocket());
                playerStatus.setStatus(Player.Status.EAT);
            } else {
                playerStatus.setStatus(Player.Status.PASS);
            }
        } else if(canGang) {
            sendMessage(player.getSocket(), ("您的手牌为：" + playerHand.printCard() + "\n").getBytes(StandardCharsets.UTF_16));
            sendMessage(player.getSocket(), ("A您可以杠或碰"  + card.printCard() + "输入 G-杠，Y-碰，P-pass\n").getBytes(StandardCharsets.UTF_16));
            String line = waitMessage(player.getSocket());
            // 删除所有杠掉的牌
            if ("G".equalsIgnoreCase(line)) {
                playerHand.getAll().removeIf(integer -> integer.equals(card.getNumber() + card.getSuit().getSalt()));
                playerHand.setCardNum(playerHand.cardNum - 3);
                playerStatus.setStatus(Player.Status.GANG);
            } else if ("Y".equalsIgnoreCase(line)){
                playerHand.getAll().removeIf(integer -> integer.equals(card.getNumber() + card.getSuit().getSalt()));
                playerHand.getAll().add(card.getNumber() + card.getSuit().getSalt());
                playerHand.getPeng().add(card.getNumber() + card.getSuit().getSalt());
                Collections.sort(playerHand.getAll());
                playerHand.setCardNum(playerHand.cardNum - 2);
                playerStatus.setStatus(Player.Status.PENG);
            } else {
                playerStatus.setStatus(Player.Status.PASS);
            }
        } else if (canPeng && canEat) {
            sendMessage(player.getSocket(), ("您的手牌为：" + playerHand.printCard() + "\n").getBytes(StandardCharsets.UTF_16));
            sendMessage(player.getSocket(), ("A您可以碰 "  + card.printCard() + "输入 Y-碰，E-吃，P-pass\n").getBytes(StandardCharsets.UTF_16));
            String line = waitMessage(player.getSocket());
            // 删除所有碰掉的牌
            if ("Y".equalsIgnoreCase(line)) {
                playerHand.getAll().removeIf(integer -> integer.equals(card.getNumber() + card.getSuit().getSalt()));
                playerHand.setCardNum(playerHand.cardNum - 2);
                playerHand.getPeng().add(card.getNumber() + card.getSuit().getSalt());
                playerStatus.setStatus(Player.Status.PENG);
            } else if("E".equalsIgnoreCase(line)) {
                eat(eatArr, playerStatus, player.getSocket());
                playerStatus.setStatus(Player.Status.EAT);
            } else {
                playerStatus.setStatus(Player.Status.PASS);
            }
        } else if (canPeng) {
            sendMessage(player.getSocket(), ("您的手牌为：" + playerHand.printCard() + "\n").getBytes(StandardCharsets.UTF_16));
            sendMessage(player.getSocket(), ("A您可以碰 "  + card.printCard() + "输入 Y-碰，P-pass\n").getBytes(StandardCharsets.UTF_16));
            String line = waitMessage(player.getSocket());
            // 删除所有碰掉的牌
            if ("Y".equalsIgnoreCase(line)) {
                playerHand.getPeng().add(card.getNumber() + card.getSuit().getSalt());
                playerHand.getAll().removeIf(integer -> integer.equals(card.getNumber() + card.getSuit().getSalt()));
                playerHand.setCardNum(playerHand.cardNum - 2);
                playerStatus.setStatus(Player.Status.PENG);
            } else {
                playerStatus.setStatus(Player.Status.PASS);
            }
        } else if (canEat) {
            sendMessage(player.getSocket(), ("您的手牌为：" + playerHand.printCard() + "\n").getBytes(StandardCharsets.UTF_16));
            sendMessage(player.getSocket(), ("A您可以吃 "  + card.printCard() + "输入 Y-吃，P-pass\n").getBytes(StandardCharsets.UTF_16));
            String line = waitMessage(player.getSocket());
            if("Y".equalsIgnoreCase(line)) {
                eat(eatArr, playerStatus, player.getSocket());
                playerStatus.setStatus(Player.Status.EAT);
            }
        }
    }


    public void eat(List<Player.TwoCard> eatArr, Player.PlayerStatus playerStatus, Socket socket) {
        if (eatArr.size() > 1) {
            StringBuilder message = new StringBuilder("A请选择吃的牌组，输入数字即可:");
            for (int i = 0; i < eatArr.size(); i ++) {
                message.append(i + 1).append("-").append(eatArr.get(i).printCard());
            }
            message.append("\n");
            sendMessage(socket, message.toString().getBytes(StandardCharsets.UTF_16));
            String msg = waitMessage(socket);
            int num;
            if (!Majiang.Card.isNum(msg) && (Integer.parseInt(msg) - 1) > eatArr.size() ) {
                // 输入错误默认选择第一组牌
                num = 0;
            } else {
                num = Integer.parseInt(msg) - 1;
            }
            playerStatus.setEatCard(eatArr.get(num));
        } else {
            playerStatus.setEatCard(eatArr.get(0));
        }

    }


    public String waitMessage(Socket socket) {
        try {
            BufferedReader ois = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while (true) {
                String line;
                while (( line = ois.readLine()) != null) {
                    return line.replace("\n", "");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
